#include "clock.h"

void Clock_Enable(void)
{
    CCM->CCGR0 = 0xFFFFFFFF;
    CCM->CCGR1 = 0xFFFFFFFF;
    CCM->CCGR2 = 0xFFFFFFFF;
    CCM->CCGR3 = 0xFFFFFFFF;
    CCM->CCGR4 = 0xFFFFFFFF;
    CCM->CCGR5 = 0xFFFFFFFF;
    CCM->CCGR6 = 0xFFFFFFFF;
}

void ARM_PLL_Init(void)
{
    // 1. 切换时钟源为24MHz CCM_CCSR  Address:0x020C400C
    // bit[8]: step_sel 0 24MHz bit[2]: step_clk 1 step_clk
    if ((((CCM->CCSR) >> 2) & 0x1) == 0)
    {
        CCM->CCSR &= ~(0x1 << 8); // 24MHz
        CCM->CCSR |= (0x1 << 2);  // step_clk
    }

    // 2. 采用无毛刺多路复用控制器,切换时钟需要等待握手完成
    for (volatile int i = 0; i < 1000; i++)
        ; // 10us

    // 3. 关闭PLL1_MAIN_CLK的电源 CCM_ANALOG_PLL_ARM address:0x020C8000
    //     bit[12]:1
    CCM_ANALOG->PLL_ARM |= (0x1 << 12);

    // 4. 配置PLL1_MAIN_CLK的时钟来源 DIV_SELECT位 关闭旁路模式 时钟输出使能
    // 4.1 bit[16]:0 禁止旁路模式 bit[15:14]:00 24MHz
    CCM_ANALOG->PLL_ARM &= ~(0x7 << 14);
    // 4.2 配置DIV_SELECT位 bit[6:0]: 0x58 0101 1000
    CCM_ANALOG->PLL_ARM &= ~(0x7F);
    CCM_ANALOG->PLL_ARM |= 0x58;
    // 4.3 关闭旁路模式 bit[16]:0
    CCM_ANALOG->PLL_ARM &= ~(0x1 << 16);
    // 4.4 时钟输出使能 bit[13]:1
    CCM_ANALOG->PLL_ARM |= (0x1 << 13);

    // 5. 配置CACRR[ARM_PODF]为2分频 CCM_CACRR Address: 0x020C4010
    // bit[31:3]:0  //bit[2:0]:001
    CCM->CACRR = 0x01;

    // 6. 重新启用PLL
    CCM_ANALOG->PLL_ARM &= ~(1 << 12);

    // 7. PLL重启后需要一定时间才能稳定输出时钟;
    for (volatile int i = 0; i < 10000; i++)
        ; // 100us

    // 8. 切换时钟为PLL1_MAIN_CLK
    CCM->CCSR &= ~(1 << 2);
}


void System_PFD_Init(void)
{
    // 1.1 配置PLL2_PFD0的输出频率为352MHz bit[5:0]
    CCM_ANALOG->PFD_528 &= ~(0x3F);
    CCM_ANALOG->PFD_528 |= 0x1B;
    // 1.2 使能PLL2_PFD0分数分频器的时钟  bit[7]
    CCM_ANALOG->PFD_528 &= ~(0x1 << 7);

    // 2.1 配置PLL2_PFD1的输出频率为594MHz bit[13:8]
    CCM_ANALOG->PFD_528 &= ~(0x3F << 8);
    CCM_ANALOG->PFD_528 |= (0x10 << 8);
    // 2.2 使能PLL2_PFD1分数分频器的时钟 bit[15]
    CCM_ANALOG->PFD_528 &= ~(0x1 << 15);

    // 3.1 配置PLL2_PFD2的输出频率为396MHz bit[21:16]
    CCM_ANALOG->PFD_528 &= ~(0x3F << 16);
    CCM_ANALOG->PFD_528 |= (0x18 << 16);
    // 3.2 使能PLL2_PFD2分数分频器的时钟 bit[23]
    CCM_ANALOG->PFD_528 &= ~(0x1 << 23);

    //PFD3可能被系统关键时钟或外设用作主时钟
    // // 4.1 配置PLL2_PFD3的输出频率为297MHz bit[29:24]
    // CCM_ANALOG->PFD_528 &= ~(0x3F << 24);
    // CCM_ANALOG->PFD_528 |= (0x20 << 24);
    // // 4.2 使能PLL2_PFD3分数分频器的时钟 bit[31]
    // CCM_ANALOG->PFD_528 &= ~(0x1 << 31);
}


void USB1_PFD_Init(void)
{
     // 1.1 配置PLL3_PFD0的输出频率为720MHz bit[5:0]
    CCM_ANALOG->PFD_480 &= ~(0x3F);
    CCM_ANALOG->PFD_480 |= 0x0C;
    // 1.2 使能PLL3_PFD0分数分频器的时钟  bit[7]
    CCM_ANALOG->PFD_480 &= ~(0x1 << 7);

    // 2.1 配置PLL3_PFD1的输出频率为540MHz bit[13:8]
    CCM_ANALOG->PFD_480 &= ~(0x3F << 8);
    CCM_ANALOG->PFD_480 |= (0x10 << 8);
    // 2.2 使能PLL2_PFD1分数分频器的时钟 bit[15]
    CCM_ANALOG->PFD_480 &= ~(0x1 << 15);

    // 3.1 配置PLL3_PFD2的输出频率为508.2MHz bit[21:16]
    CCM_ANALOG->PFD_480 &= ~(0x3F << 16);
    CCM_ANALOG->PFD_480 |= (0x11 << 16);
    // 3.2 使能PLL3_PFD2分数分频器的时钟 bit[23]
    CCM_ANALOG->PFD_480 &= ~(0x1 << 23);

    // 4.1 配置PLL3_PFD3的输出频率为454.7MHz bit[29:24]
    CCM_ANALOG->PFD_480 &= ~(0x3F << 24);
    CCM_ANALOG->PFD_480 |= (0x13 << 24);
    // 4.2 使能PLL3_PFD3分数分频器的时钟 bit[31]
    CCM_ANALOG->PFD_480 &= ~(0x1 << 31);

    
}

// AHB_CLK_ROOT=132MHz
void AHB_Clock_Init(void)
{
    // CCM_CBCMR Address:0x020C4018
    // 1. bit[19:18]: PRE_PERIPH_CLK_SEL 01 配置时钟来源为PLL2_PFD2(396MHz)
    CCM->CBCMR &= ~(0x3<<18);
    CCM->CBCMR |= (0x1<<18);

    // 2. 检查MMDC是否处于busy状态, 若为busy状态,轮询等待即可
    // CDHIPR bit[5]: PERIPH_CLK_SEL_BUSY 1代表busy 0代表空闲
    while((CCM->CDHIPR)&(0x1<<5));
    // 3. 修改CCM_CBCDR寄存器配置时钟来源硬件自动触发MMDC握手
    // CBCDR bit[25]: PERIPH_CLK_SEL 0 pll2_main_clk 
    CCM->CBCDR &= ~(0x1<<25);
    // 4. 等待握手结束
    while((CCM->CDHIPR)&(0x1<<5));

    // 5. 设置分频系数
    // 5.1 bit[1]: AHB_PODF_BUSY标志位
    while((CCM->CDHIPR)&(0x1<<1));
    // 5.2 设置分频系数为3倍频 bit[12:10]:010
    CCM->CBCDR &= ~(0x7<<10);
    CCM->CBCDR |= (0x1<<11);
    // 5.3 等待握手完成
    while((CCM->CDHIPR)&(0x1<<1));
}

// IPG_CLK_ROOT=66MHz
void IPG_Clock_Init(void)
{
    //时钟源为AHB_CLK_ROOT
    //CCM_CBCDR bit[9:8]: IPG_PODF 01 设置为2分频
    CCM->CBCDR &= ~(0x3<<8);
    CCM->CBCDR |= (0x1<<8);
}

// PERCLK_CLK_ROOT=66MHz
void Periph_Clock_Init(void)
{
    // 时钟源为IPG_CLK_ROOT
    // 配置CCM_CSCMR1 bit[6]选取时钟来源 bit[5:0]配置分频系数
    // bit[6]:0 IPG_CLK_ROOT
    CCM->CSCMR1 &= ~(0x1<<6);

    // 2. bit[5:0]:000000 设置分频系数为1分频
    CCM->CSCMR1 &= ~(0x3F); 
}


void IMX6ULL_Clock_Init(void)
{
    ARM_PLL_Init();
    System_PFD_Init();
    USB1_PFD_Init();
    AHB_Clock_Init();
    IPG_Clock_Init();
    Periph_Clock_Init();
}



